﻿namespace FSpec

open System
open System.Collections.Generic 
open System.Linq


type ShouldNotFragment() = 
     
    member this.equal expected actual = 
        verify(fun()->
            if actual |> equals expected then
                createFailureFrom Resources.TheValuesShouldNotBeEqual 
                |> addLabeledValue ("Did not expect", expected)
                |> toFailureException
            else
                null
        )
                
    member this.contain expected actual =
        verify(fun()->
            if actual |> contains expected then
                createFailureFrom (format Resources.TheCollectionShouldNotContainTheElement_0_ [expected])
                |> addLabelledValues ("Collection", actual) ("Element", expected) 
                |> toFailureException
            else
                null
        )
            
    member this.containTheString (expected:string) (actual:string) =
        verify(fun()->
            if actual |> containsTheString expected then 
                createFailureFrom (format Resources.TheStringShouldNotContainTheSubstring_0_ [expected])
                |> addLabelledValues ("String", actual)("Substring", expected)
                |> toFailureException
            else    
                null
        ) 
    
    member this.startWith (expected:string) (actual:string) =
        verify(fun()->
            if actual |> startsWith expected then
                createFailureFrom (format Resources.TheStringShouldNotStartWith_0_ [expected])
                |> addLabelledValues ("String", actual)("Substring", expected)
                |> toFailureException
            else
                null
        ) 
            
    member this.endWith (expected:string) (actual:string) =
        verify(fun()->
            if actual |> endsWith expected then
                createFailureFrom (format Resources.TheStringShouldNotEndWith_0_ [expected])
                |> addLabelledValues ("String", actual)("Substring", expected)
                |> toFailureException
            else
                null
        )
            
    member this.matchExpression (expected:string) (actual:string) =
        let options = System.Text.RegularExpressions.RegexOptions.None         
        verify(fun()->
            if actual |> matchExpression expected options then
                createFailureFrom Resources.TheStringShouldNotMatchARegularExpression 
                |> addLabelledValues ("String", actual)("Expression", expected)
                |> toFailureException
            else
                null
        ) 
    
    member this.beTrue actual =
        verify(fun()->
            if actual |> isTrue then
                createFailureFrom Resources.TheValueShouldNotBeTrue
                |> addRawValue actual
                |> toFailureException
            else
                null
        )

    member this.beFalse actual =
        verify(fun()->
            if actual |> isFalse then
                createFailureFrom Resources.TheValueShouldNotBeFalse
                |> addRawValue actual
                |> toFailureException
            else
                null
        )

    member this.beGreaterThan expected actual  =
        verify(fun() ->
            if actual |> isGreaterThan expected then
                createFailureFrom Resources.TheValueShouldNotBeGreaterThanThis
                |> addLabelledValues ("LHS", actual)("RHS", expected)
                |> toFailureException
            else
                null
        ) 
                 
    member this.beLessThan expected actual =
        verify(fun() ->
            if actual |> isLessThan expected then
                createFailureFrom Resources.TheValueShouldNotBeLessThanThis
                |> addLabelledValues ("LHS", actual)("RHS", expected)
                |> toFailureException
            else
                null
        )
                
    member this.beLessThanOrEqualTo expected actual =
        verify(fun() ->
            if actual |> isLessThanOrEqualTo expected then
                createFailureFrom Resources.TheValueShouldNotBeLessThanOrEqualToThis 
                |> addLabelledValues ("LHS", actual)("RHS", expected)
                |> toFailureException
            else
                null
        )
        
    member this.beGreaterThanOrEqualTo expected actual =
        verify(fun() ->
            if actual |> isGreaterThanOrEqualTo expected then
                createFailureFrom Resources.TheValueShouldNotBeGreaterThanOrEqualToThis 
                |> addLabelledValues ("LHS", actual)("RHS", expected)
                |> toFailureException
            else
                null
        )
                
    member this.beTheSameAs expected actual =
        verify(fun() ->
            if actual |> isTheSameAs expected then
                createFailureFrom Resources.TheValuesShouldNotBeTheSame 
                |> addLabeledValue ("Did not expect", expected)
                |> toFailureException
            else
                null
        )

    member this.beEmpty<'t> (collection:IEnumerable<'t>) =
        verify(fun() ->
            if not(collection |> isEmpty) then 
                null
            else
                createFailureFrom Resources.TheCollectionShouldNotBeEmpty
                |> addRawValue collection    
                |> toFailureException
        )

    member this.beBetween lower conjunction upper boundType value =
        verify(fun() ->
            if not(value |> isBetween lower conjunction upper boundType) then 
                null 
            else
                let failure =
                    match boundType with
                    | Inclusive ->
                        createFailureFrom (format Resources.TheValueShouldNoBeWithin_0_And_1_Inclusive [lower; upper]) 
                    | Exclusive ->
                        createFailureFrom (format Resources.TheValueShouldNoBeWithin_0_And_1_Exclusive [lower; upper])
                failure
                |> addLabeledValue ("Value", value)
                |> addLabeledValue ("Upper bound", upper)
                |> addLabeledValue ("Lower bound", lower)
                |> toFailureException
        )

    member this.beWithin tolerance conjunction expectedValue value =
        verify(fun()->
            let deviation = abs(value - expectedValue)
            if not(value |> isWithin tolerance Of expectedValue) then
                null
            else
                createFailureFrom (format
                                    Resources.TheValueShouldNotBeWithin_0_of_1_ 
                                    [tolerance;expectedValue])
                |> addLabeledValue("Expected", expectedValue)
                |> addLabeledValue("Actual", value)
                |> addLabeledValue("Tolerance", tolerance)
                |> addLabeledValue("Deviation From Expected", deviation)
                |> addLabeledValue("Deviation From Tolerance", abs(tolerance - deviation))
                |> toFailureException
        )
    
    member this.throw (exceptionType:Type) (func:unit->unit) =
        verify(fun() ->
            if func |> throws exceptionType then
                createFailureFrom (
                    format 
                        Resources.TheExpressionShouldNotThrowA_0_  
                        [exceptionType]
                )
                |> toFailureException 
            else   
                null
        )

    member this.failwith (message) (func:unit->'a)= 
        verify(fun() ->
            if func |> failsWith message then
                createFailureFrom (format Resources.TheExpressionShouldNotFailWithTheMessage_0_ [message])
                |> toFailureException
            else
                null
        )

    member this.inheritFrom (t:System.Type) value =
        verify(fun()->
            if not(t.IsAssignableFrom (value.GetType())) then
                null
            else
                createFailureFrom (format Resources.TheObjectShouldNotInheritFromType_0_ [t])
                |> toFailureException
        )

    member this.beA expectedType instance = 
        verify(fun()->
            if instance |> isA expectedType then
                createFailureFrom (format
                                    "The actual value was not expected to be an instance of the type {0}"
                                    [expectedType])
                |> addLabelledValues("Actual", instance)("UnexpectedType", expectedType)
                |> toFailureException
            else
                null
        )

    member this.beNull value =
        verify(fun()->
            if value |> isNull then
                createFailureFrom(Resources.TheValueShouldNotBeNull)
                |> toFailureException
            else
                null
        )

    member this.ignoreCase = new IgnoreCaseFragment(false)